package com.illtamer.infinite.morii.service;

import com.illtamer.infinite.morii.pojo.*;
import com.illtamer.infinite.morii.util.CommonUtil;
import com.illtamer.infinite.morii.util.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Date;

@Slf4j
@Service
public class TransportService {

    @Resource(name = "moriiVerifyService")
    private MoriiVerifyService verifyService;

    @Resource(name = "moriiObjectService")
    private MoriiObjectService objectService;

    public boolean upload(String groupId, String artifactId, String version, byte[] jar, byte[] pom) {
        String key = groupId + '/' + artifactId + '/' + version;
        MoriiVerify verify = verifyService.query(key);
        MoriiObject targetObject = new MoriiObject(null, key, pom, jar);
        if (verify != null) {
            if (SecurityUtil.SHA1(jar).equals(verify.getJarSHA1())
                        && SecurityUtil.SHA1(pom).equals(verify.getPomSHA1())) {
                return false;
            }
        } else {
            // asc
            String pomASC = SecurityUtil.ASC(pom);
            String jarASC = SecurityUtil.ASC(jar);
            // md5
            String pomMD5 = SecurityUtil.MD5(pom);
            String jarMD5 = SecurityUtil.MD5(jar);
            // sha1
            String pomSHA1 = SecurityUtil.SHA1(pom);
            String jarSHA1 = SecurityUtil.SHA1(jar);
            verify = new MoriiVerify(null, groupId, artifactId, version, new Date(), jar.length, pom.length,
                    pomASC, pomMD5, pomSHA1, jarASC, jarMD5, jarSHA1, null);
        }
        verifyService.insert(verify);
        objectService.insert(targetObject);
        return true;
    }

    public boolean download(String uri, HttpServletRequest request, HttpServletResponse response) {
        // .jar content-type: application/java-archive
        // .asc .md5 .sha1 content-type: text/plain
        final String[] keys = CommonUtil.splitKey(uri, 4);
        final MoriiVerify verify = verifyService.query(keys[0].replace('/', '.') + '/' + keys[1] + '/' + keys[2]);
        if (verify == null) return false;
        MoriiFile moriiFile = getMoriiFile(keys[3], verify);
        if (moriiFile == null) return false;
        try {
            doDownload(moriiFile, request, response);
        } catch (IOException e) {
            log.warn("Unexpected error", e);
        }
        return true;
    }

    private void doDownload(MoriiFile file, HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setHeader("Content-Disposition", "attachment; fileName=" + URLEncoder.encode(file.getName(), "UTF-8"));
        response.setContentType("application/octet-stream");
        response.setHeader("Accept-Ranges", "bytes");
        String range = request.getHeader("Range");

        int fromPos = 0, toPos, size;
        if (range == null) {
            response.setHeader("Content-Length", Long.toString(file.getLength()));
            size = file.getLength();
        } else {
            response.setStatus(206);
            String[] array = range.substring("bytes=".length()).split("-");
            fromPos = Integer.parseInt(array[0]);
            toPos = (array.length == 2) ? Integer.parseInt(array[1]) : 0;
            size = (toPos > fromPos) ? (toPos - fromPos + 1) : (file.getLength() - fromPos);
            response.setHeader("Content-Length", Long.toString(size));
        }

        try (
                OutputStream output = response.getOutputStream();
        ) {
            output.write(file.getData(), fromPos, size-fromPos);
            output.flush();
            response.flushBuffer();
        }
    }

    private MoriiFile getMoriiFile(String name, MoriiVerify verify) {
        String[] firstSplitSuffix = CommonUtil.splitSuffix(name);
        if (firstSplitSuffix[1].length() == 0) return null;
        final VerifyEnum verifyEnum = VerifyEnum.getVerifyEnum(firstSplitSuffix[1]);
        FileTypeEnum fileTypeEnum;
        if (verifyEnum == null) {
            fileTypeEnum = FileTypeEnum.getFileTypeEnum(firstSplitSuffix[1]);
        } else { // verifyEnum != null
            String[] secondSplitSuffix = CommonUtil.splitSuffix(firstSplitSuffix[0]);
            if (secondSplitSuffix[1].length() == 0) return null;
            fileTypeEnum = FileTypeEnum.getFileTypeEnum(secondSplitSuffix[1]);
        }
        if (fileTypeEnum == null) return null;

        MoriiFile file = new MoriiFile();
        file.setName(name);
        if (verifyEnum != null) {
            file.setVerify(true);
            file.setData(verify.getVerifyMap().get(fileTypeEnum)[verifyEnum.getIndex()]);
        } else {
            final MoriiObject object = objectService.query(verifyService.getMoriiVerifyKey(verify));
            file.setVerify(false);
            if (fileTypeEnum == FileTypeEnum.JAR) {
                file.setData(object.getJar());
            } else {
                file.setData(object.getPom());
            }
        }

        file.setLength(file.getData().length);
        return file;
    }

}
